﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using DataReader;

namespace PrmReader
{
    //SMS Parameter file
    public class PrmFile
    {
        public struct PrmData
        {
            public enum DataTypes : uint
            {
                NULL = 0,
                Byte = 1,
                Word = 2,
                Int = 3,
                Single = 4,
                Double = 5
            }

            private DataTypes type;
            private byte[] value;

            public int Size {
                get {
                    switch (type){
                        case DataTypes.Byte:
                            return 1;
                        case DataTypes.Single:
                            return 4;
                        default: 
                            return 0;
                    }
                }
            }
            public DataTypes DataType
            {
                get { return type; }
            }

            public PrmData(Stream s)
            {
                type = DataTypes.NULL;
                value = null;

                Read(s);
            }

            public void Read(Stream s)
            {
                type = (DataTypes)Data.ReadUInt32(s);
                value = Data.ReadEndian(s, Size);
            }
            public void Write(Stream s)
            {
                Data.WriteInt32(s, (int)type);
                Data.WriteEndian(s, value);
            }

            public ValueType GetData()
            {
                if (value.Length != Size)
                    throw new Exception("PRM: Invalid type matchup.");

                switch (type)
                {
                    case DataTypes.Byte:
                        return value[0];
                    case DataTypes.Word:
                        return BitConverter.ToInt16(value, 0);
                    case DataTypes.Int:
                        return BitConverter.ToInt32(value, 0);
                    case DataTypes.Single:
                        return BitConverter.ToSingle(value, 0);
                    case DataTypes.Double:
                        return BitConverter.ToDouble(value, 0);
                    default:
                        throw new Exception("PRM: Unknown data format.");
                }
            }
            public void SetData(ValueType obj)
            {
                switch (type)
                {
                    case DataTypes.Byte:
                        value = BitConverter.GetBytes((byte)obj);
                        break;
                    case DataTypes.Word:
                        value = BitConverter.GetBytes((Int16)obj);
                        break;
                    case DataTypes.Int:
                        value = BitConverter.GetBytes((Int32)obj);
                        break;
                    case DataTypes.Single:
                        value = BitConverter.GetBytes((Single)obj);
                        break;
                    case DataTypes.Double:
                        value = BitConverter.GetBytes((Double)obj);
                        break;
                    default:
                        throw new Exception("PRM: Unknown data format.");
                }
            }
        }

        private FileStream file;    //File Stream
        private Dictionary<string, PrmData> paramList = new Dictionary<string, PrmData>();  //Parameters

        private string path;

        public string Name
        {
            get
            {
                FileInfo fi = new FileInfo(path);
                return fi.Name;
            }
        }

        /*
         * Creates empty, nameless file
         */
        public PrmFile()
        {
            paramList = new Dictionary<string, PrmData>();
            file = null;
        }

        /*
         * Creates object and loads from file
         */
        public PrmFile(string filename)
        {
            paramList = new Dictionary<string, PrmData>();
            file = null;
            Load(filename);
        }

        /*
         * Loads prm file type
         */
        public void Load(string filename)
        {
            path = filename;

            if (!File.Exists(filename))
                return;
            file = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);

            try
            {
                int count = Data.ReadInt32(file);
                for (int i = 0; i < count; i++)
                {
                    UInt16 hash = Data.ReadUInt16(file);
                    UInt16 strSize = Data.ReadUInt16(file);
                    string key = Data.ReadString(file, strSize);

                    PrmData dati = new PrmData(file);

                    paramList.Add(key, dati);
                }
            }
            catch
            {
                file.Close();
                paramList.Clear();
                return;
            }
        }

        /*
         * Gets target file size
         */
        public int GetSize()
        {
            int size = 4;
            foreach (KeyValuePair<string, PrmData> kvp in paramList)
                size += 8 + Encoding.GetEncoding("shift-jis").GetByteCount(kvp.Key) + kvp.Value.Size;
            return size;
        }

        /*
         * Saves to new file
         */
        public void Save(string filename)
        {
            file.Close();
            file = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite);
            path = filename;
            Save();
        }

        /*
         * Saves prm file type
         */
        public void Save()
        {
            file.SetLength(GetSize());
            file.Seek(0, SeekOrigin.Begin);
            Data.WriteInt32(file, paramList.Count);
            foreach (KeyValuePair<string, PrmData> kvp in paramList)
            {
                Data.WriteUInt16(file, GCN.CreateHash(kvp.Key));
                Data.WriteUInt16(file, (UInt16)Encoding.GetEncoding("shift-jis").GetByteCount(kvp.Key));
                Data.WriteString(file, kvp.Key, Encoding.GetEncoding("shift-jis"), false);
                kvp.Value.Write(file);
            }
            file.Flush();
        }

        public ValueType GetParameter(string key)
        {
            return paramList[key].GetData();
        }
        public void SetParameter(string key, ValueType obj)
        {
            paramList[key].SetData(obj);
        }

        public string[] GetAllKeys()
        {
            List<string> keys = new List<string>();
            foreach (KeyValuePair<string, PrmData> kvp in paramList)
                keys.Add(kvp.Key);
            return keys.ToArray();
        }

        public PrmData.DataTypes GetDataType(string key)
        {
            return paramList[key].DataType;
        }

        public void UnLoad()
        {
            if (file != null)
                file.Close();
            paramList.Clear();
        }
    }
}
